﻿using System;
using System.Collections.Generic;
using System.Linq;
using InvestmentIntelligence.DbModel.Models;

namespace InvestmentIntelligence.Data.Repository.EFImplementation
{
    using System.Data.Entity;

    public class UserRepository : IIPRepository<User>, IUserRepository
    {
        public new int Add(User user)
        {
            base.Add(user);
            return user.Id;
        }

        public List<T> GetUserList<T>(Func<IQueryable<User>, IEnumerable<T>> f)
        {
            return
                GetList(
                    dc => f(dc.Include("Contact").Include("User2Role.Role").Include("User2Role.Role.Entity")))
                    ;
        }

        public User GetUser(int userId)
        {
            return Get(c => c
                       .Include("Contact")
                       .Include("User2Role.Role")
                       .Include("User2Role.Role.Entity")                       
                       .Single(x => x.Id == userId));
        }

        public User GetUser(string loginName)
        {
            return
                Get(c => c
                    .Include("Contact")
                    .Include("USer2Role.Role")
                    .Include("USer2Role.Role.Entity")
                    .SingleOrDefault(
                        x => x.LoginName.Equals(loginName.Trim(), StringComparison.InvariantCultureIgnoreCase)));
        }

        public User GetUserByEmail(string emailAddress)
        {
            return
                Get(c => c
                    .Include("Contact")
                    .Include("USer2Role.Role")
                    .Include("USer2Role.Role.Entity")
                    .SingleOrDefault(
                        x => x.Contact.Email.Equals(emailAddress.Trim(), StringComparison.InvariantCultureIgnoreCase)));
        }

        public void UpdateUser(User user)
        {
            RunUnitOfWork(data =>
                              {
                                  var actualUser = data.Users
                                      .Include("Contact")
                                      .SingleOrDefault(x => x.Id == user.Id);

                                  if (actualUser == null)
                                  {
                                      throw new NullReferenceException("User no exist");
                                  }

                                  actualUser.Contact.Email = user.Contact.Email;
                                  actualUser.Contact.FirstName = user.Contact.FirstName;
                                  actualUser.Contact.LastName = user.Contact.LastName;
                                  actualUser.Contact.Mobile = user.Contact.Mobile;
                                  actualUser.Contact.Tel = user.Contact.Tel;
                                  actualUser.Contact.Title = user.Contact.Title;
                                  data.SaveChanges();
                                  return default(User);
                              });
        }

        public void UpdateUserPassword(int userId, string password)
        {
            RunUnitOfWork(data =>
                                {
                                    var actualUser = data.Users.Single(x => x.Id == userId);
                                    actualUser.Password = password;
                                    data.SaveChanges();
                                    return default(User);
                                });
        }

        public Role GetRole(int id)
        {
            return InternalGet(c => c.Roles, v => v.Single(x => x.Id == id));
        }

        public Role GetRoleWithUsers(int id)
        {
            return InternalGet(c => c.Roles.Include("User2Role.User"), v => v.Single(x => x.Id == id));
        }

        public List<T> GetRoles<T>(Func<IQueryable<Role>, IEnumerable<T>> f)
        {
            return InternalGet(dc => dc.Roles
                       .Include("Entity"), z => f(z).ToList());
        }

        public List<T> GetRoles<T>(Func<IQueryable<Role>, IEnumerable<T>> f, int entityId)
        {
            return InternalGet(dc => dc.Roles.Where(x => x.EntityId == entityId), z => f(z).ToList());
        }

        public void RemoveRole(int id)
        {
            RunUnitOfWork(data =>
            {
                var role = data.Roles.Find(id);
                foreach (var user2Role in role.User2Role.ToList())
                {
                    data.User2Role.Remove(user2Role);
                }
                data.Roles.Remove(role);
                data.SaveChanges(); return default(Role);
            });
        }

        public int AddRole(Role role)
        {
            var tempRole = InternalGet(c =>
                c.Roles, v => v.SingleOrDefault(x => (role.EntityId != 0 ? x.EntityId == role.EntityId : x.EntityId == null)
                                                &&
                                                x.Title.Equals(role.Title.Trim(),
                                                               StringComparison.InvariantCultureIgnoreCase)));
            if (tempRole != null)
            {
                throw new ArgumentException("Role with same Name already exists");
            }
            return RunUnitOfWork(data =>
                               {
                                   data.Roles.Add(role);
                                   data.SaveChanges();
                                   return role.Id;
                               });
        }

        public void AttachRoleToUser(int userId, int roleId)
        {
            GetRole(roleId);
            RunUnitOfWork(data =>
                              {
                                  var user = data.Users.Single(x => x.Id == userId);
                                  var user2Role = new User2Role
                                                      {
                                                          RoleId = roleId,
                                                          UserId = userId,
                                                          StartDt = DateTime.Now,
                                                          EndDt = DateTime.Now.AddYears(1)
                                                      };

                                  user.User2Role.Add(user2Role);
                                  data.SaveChanges();
                                  return user;
                              });
        }

        public void RemoveRoleFromUser(int userId, int roleId)
        {
            RunUnitOfWork(data =>
                              {
                                  var roleToUser = data.User2Role.Single(x => x.RoleId == roleId && x.UserId == userId);
                                  data.User2Role.Remove(roleToUser);
                                  data.SaveChanges();
                                  return roleToUser;
                              });
        }

        public List<User> GetSharedUsers(int userId)
        {
            var actualUser = Get(c => c.Include("AnalysisGroups")
                                 .Include("AnalysisGroups.Users")
                                 .Include("AnalysisGroups.Users.Contact")
                                 .Include("AnalysisGroups.Users.User2Role.Role")
                                 .Include("AnalysisGroups.Users.User2Role.Role.Entity")
                                 .Single(x => x.Id == userId));
            return actualUser.AnalysisGroups.SelectMany(x => x.Users).GroupBy(x => x.Id).Select(x => x.First()).ToList();
        }

        public List<int> GetSharedFundBooksForUser(int userId)
        {
    return GetList(users => 
                from user in users
                from analysisGroup in user.AnalysisGroups
                from fundBook in analysisGroup.FundBookEntities
                where user.Id == userId
                select fundBook.Id
                );
            //var actualUser = Get(c => c.Include("AnalysisGroups")
            //                                         .Include("AnalysisGroups.Users")
            //                                         .Include("AnalysisGroups.Users.AnalysisGroups")
            //                                         .Include("AnalysisGroups.Users.AnalysisGroups.FundBookEntities")
            //                   .Single(x => x.Id == userId));

            //return actualUser.AnalysisGroups.SelectMany(x => x.Users)
            //                                          .SelectMany(x => x.AnalysisGroups)
            //                                          .SelectMany(x => x.FundBookEntities)
            //                                          .Select(x => x.Id)
            //                                          .Distinct()
            //                                          .ToList();        }
        }
    }
}